%{ open Ast %}

%token LPAREN RPAREN LBRACE RBRACE LBRACKET RBRACKET
%token SEMI COMMA DOT
%token PLUS MINUS TIMES DIVIDE MOD PLUSPLUS MINUSMINUS
%token PLUSEQ MINUSEQ TIMESEQ DIVIDEEQ MODEQ
%token EQ NEQ LT LEQ GT GEQ AND NOT OR ASSIGN
%token IF ELSE ELSEIF FOR WHILE RETURN 
%token INT VOID FLOAT BOOL NOTE CHORD STAFF PART 
%token <int> INTLITERAL
%token <float> FLOATLITERAL
%token <bool> BOOLLITERAL
%token <string> ID
%token <string> DATATYPE
%token <string> PITCHLITERAL
%token EOF

%nonassoc NOELSE
%nonassoc ELSE
%nonassoc LPAREN
%left PLUSEQ MINUSEQ
%left TIMESEQ DIVIDEEQ MODEQ
%right ASSIGN
%left OR
%left AND 
%left EQ NEQ
%left LT GT LEQ GEQ
%right NOT
%left PLUS MINUS
%left TIMES DIVIDE MOD
%left PLUSPLUS MINUSMINUS
%start program
%type <Ast.program> program

%%

program:
/* nothing */ { [], [] }
| program vdecl { ($2 :: fst $1), snd $1 }
| program fdecl { fst $1, ($2 :: snd $1) }

fdecl:
	DATATYPE ID LPAREN formals_opt RPAREN LBRACE vdecl_list stmt_list RBRACE
		{ {	fname = $2;
			rettype = $1;
			formals = $4;
			locals = List.rev $7;
			body = List.rev $8 } }

formals_opt:
	/* nothing */ { [] }
	| formal_list { List.rev($1) }

formal_list:
	param_decl { [$1] }
	| formal_list COMMA param_decl { $3 :: $1 }
			
vdecl_list:
	/* nothing */    { [] }
	| vdecl_list vdecl { $2 :: $1 }

vdecl:
	DATATYPE ID SEMI { { varname = $2; vartype = $1 } }
	
param_decl:
	DATATYPE ID
		{ {	paramname = $2;
			paramtype = $1 } }

stmt_list:
	/* nothing */ { [] }
	| stmt_list stmt { $2 :: $1 }
	
stmt:
	expr SEMI { Expr($1) }
	| RETURN expr_opt SEMI { Return($2) }
	| LBRACE stmt_list RBRACE { Block(List.rev $2) }
	| IF LPAREN expr RPAREN stmt %prec NOELSE { If($3, $5, Block([])) }
	| IF LPAREN expr RPAREN stmt ELSE stmt %prec ELSE { If($3, $5, $7) }
	| FOR LPAREN expr_opt SEMI expr_opt SEMI expr_opt RPAREN stmt { For($3, $5, $7, $9) }
	| WHILE LPAREN expr RPAREN stmt { While($3, $5) }

expr_opt:
	/* nothing */ { NoExpr }
	| expr { $1 }	

expr:
	ID { Id($1) }														//x
	| ID DOT ID { MemberAccess($1, $3) }								//note.pitch
	| INTLITERAL { IntLiteral($1) }										//1
	| FLOATLITERAL { FloatLiteral($1) }									//1.0
	| BOOLLITERAL { BoolLiteral($1) }									//true
	| PITCHLITERAL { PitchLiteral ($1) }								//As7
	| expr ASSIGN expr { Assign($1,$3) }								//x = y
	| expr PLUSEQ expr { Assign($1, BinOp($1, Add, $3)) }				//x += y
	| expr MINUSEQ expr { Assign($1, BinOp($1, Sub, $3)) }				//x -= y
	| expr TIMESEQ expr { Assign($1, BinOp($1, Mult, $3)) }				//x *= y
	| expr DIVIDEEQ expr { Assign($1, BinOp($1, Div, $3)) }				//x /=y
	| expr MODEQ expr { Assign($1, BinOp($1, Mod, $3)) }				//x %= y
	| expr PLUS expr { BinOp($1, Add, $3) }								//x + y
	| expr MINUS expr { BinOp($1, Sub, $3) }							//x - y
	| expr TIMES expr { BinOp($1, Mult, $3) }							//x * y
	| expr DIVIDE expr { BinOp($1, Div, $3) }							//x / y
	| expr MOD expr { BinOp($1, Mod, $3) }								//x % y
	| expr AND expr { BinOp($1, And, $3) }								//x && y
	| expr OR expr { BinOp($1, Or, $3) }								//x || y
	| expr EQ expr { BinOp($1, Eq, $3) }								//x == y
	| expr NEQ expr { BinOp($1, NEq, $3) }								//x != y
	| expr LT expr { BinOp($1, Less, $3) }								//x < y
	| expr LEQ expr { BinOp($1, LEq, $3) }								//x <= y
	| expr GT expr { BinOp($1, Greater, $3) }							//x > y
	| expr GEQ expr { BinOp($1, GEq, $3) }								//x >= y
	| NOT expr { Not($2) }												//!x	                                      					 
	| expr PLUSPLUS { Assign($1, BinOp($1, Add, IntLiteral(1))) }		//x++
	| expr MINUSMINUS { Assign($1, BinOp($1, Sub, IntLiteral(1))) }		//x--
	| LPAREN expr RPAREN { $2 }											//(x), expression in parenthesis is the same as the expression
	| ID LPAREN actuals_opt RPAREN { Call($1, $3) }						//x(...), function call

actuals_opt:
	/* nothing */ { [] }
	| actuals_list { List.rev $1 }

actuals_list:
	expr { [$1] }
	| actuals_list COMMA expr { $3 :: $1 }